学习vim 第3章
start at 2021/10/04?

第三章 文件查找

这一章的目标是向你介绍如何在VIM里进行文件搜索。学会如何快速搜索文件会让你用VIM的生产力大幅提升。当我还在想象如何快速搜索文件的时候,我已经做出了全时间使用VIM的改变。

这章分成两个部分:如何不用任何插件搜索文件 和 如何用fzf.vim 插件去搜索文件。让我们开始吧

打开和编辑文件

想要在VIM里打开一个文件,你可以用:edit

:edit file.txt

如果file.txt存在,它就会打开file.txt的buffer。如果不存在,它就会为这个文件创建一个新的buffer。

:edit可以用<tab>自动补全,举个例子,如果你的文件是Rails的用户控制器,文件路径是./app/controllers/users_controllers.rb。你可以用<tab>来快速找到

:edit a<tab>c<tab>u<tab>

:edit支持通配符语法*匹配任意文件在当前文件夹。如果你想要在当前文件夹找一个.yml后缀的文件,可以

:edit *.yml<tab>

VIM会给你一个所有在当前文件夹的.yml后缀的文件列表。

你可以用**进行递归式搜索,如果你想要在你的项目里找到所有的*.md文件,但你不知道它们在哪一个文件夹里,你就可以

:edit **/*.md<tab>

:edit可以运行netrw,一个VIM内置的文件浏览功能。想要调出这个界面要用:edit去打开一个文件夹而不是文件。

:edit .
:edit test/unit/

用Find来查找文件

你可以用:find来查找文件,举个例子

:find package.json
:find app/controlers/users_controller.rb

这个指令同样可以用<tab>补全,例子就不举了。

你可以觉得:find:edit是一样的,那它们到底有什么不同呢?

Find 和 Path

不同的地方就是:find通过path来查找文件。让我们来学习一点path的知识。一旦你学会了如何修改你的路径,:find会成为一个强大的工具,查看当前的路径,你可以

:set path?

一般情况,你的路径可能会是这样

path=.,/usr/include,,
  1. .的意思是在当前文件所在的文件夹里搜索。
  2. ,的意思是在当前的文件夹搜索、
  3. /usr/include是C语言头文件的文件夹。

前两个点很重要,第三个点现在可以忽略。最重要的是你可以自己修改你的路径,让我们来假设下面是你的工程结构

app/
  assets/
  controllers/
    application_controller.rb
    comments_controller.rb
    users_controller.rb
    ...

如果你想从根目录前往user_controller.rb,你你需要穿过很多个文件夹(按一堆tab键)。当我们在写一个框架的时候,你大概会花百分之九十的时间在某个文件夹里,在这种情况下,你只想少敲点按键打开在controllers文件夹里的文件。而path能让这个过程缩短很多。

你需要在你的路径里面添加app/controllers/

:set path+=app/controllers/

现在你的路径已经更新了,当你输入:find u<Tab>,VIM现在会直接在app/controllers/文件夹里面找开头是u的文件。

如果你在controllers/又嵌套了文件夹,像app/controllers/account/users_controller.rb,VIM不会找到user_controllers,你需要改变增加的地址,:set path+=app/controllers/**,这样自动补全就会帮你找到users_controller.rb。这就很棒了,你现在只需要按一下tab键就可以找到user_controllers了。

你可以在想在path上添加整个工程文件的地址,这样你按下tab键就可以访问到所有的文件。像这样

:set path+=&PWD/**

$PWD是当前的工作文件夹,如果你想添加完整的项目到path时,你的项目最好规模不是很大,当你的文件夹里文件很多的时候,会发现也很难处理。所以我推荐你只加上经常访问的文件路径。

你可以在你的vimrc里面添加set path+={your-path-here},更新path就只需要短短你几秒,却能大大节省你的时间。

用Grep来文件内搜索

如果你需要在文件内查找(在很多文件里面找一个词语),你可以用grep。VIM又两种方法打开grep

  1. 内部grep(:vim,没错,是:vimgrep的缩写)。
  2. 外部grep(:grep)。

让我们先来学习一下内部grep,:vim又下面这样的语法:

:vim /pattern/ file
  1. /pattern/是你要查找的内容
  2. file是文件名,你也可以输入多个文件名。VIM会在输入的文件里面查找你要找的内容。类似:find,你也可以用***这两个通配符。

举个例子,当你想要在app/controllers/的所有ruby文件中查找”breakfast”时,你可以

:vim /breakfast/ app/controllers/**/*.rb

在运行这条指令之后,你会被重定向到第一个查找结果,VIM的vim搜索用了quickfix操作。想要看到所有的搜索结果,需要运行:copen,这会打开一个quickfix窗口。下面有一些有用的quickfix指令。

:copen		"打开quickfix窗口
:cclose		"关闭quickfix窗口
:cnext		"进入下一个查找结果
:cprevious	"进入前一个查找结果
:colder		"进入之前的查找列表
:cnewer		"进入新的查找列表

如果你想了解更多quickfix,可以在:h quickfix查看。

你可能注意到使用internal grep(:vim)在你有很多匹配数量的时候查找很慢。这是因为VIM把搜索目标都读进了内存。VIM像要编辑一样地加载了每个匹配文件。如果有很多查找目标,VIM就会花费大量地内存空间。

让我们再来看看external grep。默认情况下,用的是grep命令行指令。如果你想要在一个app/controllers路径地ruby文件里查找”lunch”,你就可以用

:grep -R "lunch" app/controllers/

你会发现与:vim指令不同,grep用的是”pattern”,而不知/pattern/,它也会在quickfix里显示。

VIM用grepprg变量来确认哪些外部程序要在:grep运行时运行。所以你没有必要用命令行地grep指令。等会我会向你展示如何改变默认地grep外部程序。

用Netrw来浏览文件系统

netrw是VIM内置地文件浏览系统。它经常用来查看项目的层次结构。想要运行netrw,你需要在你的.vimrc里面添加这两个选项

set nocp
filetype plugin on

因为netrw是很庞大的体系,所以我只会教一些基础的语法,但是这就足够你起步使用了。当你运行vim或者在后面加一些文件夹的指令就能打开netrw

vim .
vim src/client/
vim app/controllers/

想要在已经运行了VIM打开netrw的话,可以用:edit来打开它

:edit .
:edit src/client/
:edit app/controllers/

下面是一些打开netrw窗口的其他方法。

:Explore	"在当前文件里启动netrw
:Sexplore	"不是黄色笑话。是split+explore,垂直分屏打开netrw
:Vexplore	"这个就是水平分屏了

你可以用VIM motions来使用netrw(在很后面的章节会介绍motion)。如果你需要创建,删除,和重命名文件/文件夹,下面有一些有用的指令。

%		"创建一个新的文件
d		"创建要给新的文件夹
R		"重命名一个文件/文件夹
D		"删除一个文件/文件夹

:h netrw里的说明很容易理解,如果你有时间可以看一看。

如果你觉得netrw看上去太朴素了,想要更浓的味,可以试试netrw的提升插件 vim-vinegar。如果你想找个其他的文件浏览器,NERDTree也是一个不错的改版,可以看看。

Fzf

现在你已经知道了如何用内置工具去检索查找文件了,让我们来学学用插件怎么玩。

VIM有一点做的没现代的编辑器好,那就是不管是查找文件还是在文件内查找,模糊搜索对VIM来说都有点困难。在后半章里,我会向你展示如何用fzf.vim 让搜索变得又强大又简单。

安装

首先,确保你已经下载了fzfripgrep 。按照github上面的说明repo。如果你弄好了,你应该可以使用fzfrg命令已经可以使用了。

Ripgrep是一个很像grep的工具(从名字上就可以看出来)。它一般情况下都会比grep快,并且有很多优点。Fzf是一个泛用的命令行模糊搜索指令。你可以把它和其他指令一起使用,包括ripgrep。它们一起使用后,就会成为一个强大的文件搜索工具。

Fzf初始是不用ripgrep的,所以我们需要通过FZF_DEFAULT_COMMAND变量告诉fzf使用ripgrep。在我的.zshrc里(如果你是bash在你的.bashrc)添加

if type rg &> /dev/null;then
    export FZF_DEFAULT_COMMAND='rg --files'
    export FZF_DEFAULT_OPTS='-m'
fi

注意第三行的FZF_DEFAULT_OPTS='-m',这个选项让我们可以用<Tab>或者<Shift-Tab>来多重选择。这行没法用在VIM上,但我认为这是很有用的选项。当你想在多个文件中执行搜索和替换时,它会派上大用场,我会在稍后做一些简单的介绍。Fzf还有很多选项,但我不会在这里将它们一 一介绍,想知道更多关于fzf的知识,可以查看man fzf或者 fzf’s repo。首先确保有有了export FZF_DEFAULT_COMMAND='rg'

装完了fzf和ripgrep之后,让我们来设置fzf的插件,我这个例子中用的是vim-plug 插件管理器。但你也可以用其他插件。

把下面几句话添加进你的.vimrc,你需要用fzf.vim插件(出自fzf作者之手)。

Plug 'junegunn/fzf.vim'
Plug 'junegunn/fzf',{'do':{-> fzf#install()}}

在你添加了这些之后,你需要在vim里运行:PlugInstall,它会安装所有在vimrc里那些没被安装的插件。我们这次的话会安装上fzf.vimfzf

先知道更多这个插件的信息?fzf.vim repo

Fzf语法

想要高效地使用fzf,你应该学习一些基础的fzf语法。别担心,学习清单就一点点:

  1. ^是前缀精准匹配符。想要搜索以”welcome”开头的可以用^welcome
  2. $是后缀精准匹配符。想要搜索以”my friends”结尾的,可以用friends$
  3. '是一个精准匹配符。想要搜索”welcome my friends”可以用'welcome my friends
  4. |是或匹配符,想要搜索”friends”和”foes”可以用friends | foes
  5. !是非匹配符,当你想要搜索有”welcome”但是不能有”friends”时,welcome !friends

你可以混合这些符号,比如说,^hello | ^welcome friends$会匹配到一句以”welcome”或”hello”开头,以”friends”结尾的话。

查找文件

想要在VIM里用fzf插件搜索文件,你可以用:Files指令。运行:Files之后,一个搜索面板会立即出现在你的屏幕上。

如果你用这个指令很频繁,我的建议是在vimrc里设置快捷键,我设置为Ctrl-f

nnoremap <silent><C-f> :Files<CR>

在files中搜索

想要在多个文件中搜索,你可以使用:Rg指令。

同样的,当你很频繁地使用时,可以设置为快捷键。我设置成了 <Leader>f(前缀键+f,前缀键貌似默认是反斜杠)。

nnoremap <silent><Leader>f :Rg<CR>

其他搜索

Fzf.vim 提供了多种搜索指令,我不会一一介绍,但你可以在这里学习它们。

下面是我的fzf键盘映射

nnoremap <silent> <Leader>b :Buffers<CR>
nnoremap <silent> <C-f> :Files<CR>
nnoremap <silent> <Leader>f :Rg<CR>
nnoremap <silent> <Leader>/ :BLines<CR>
nnoremap <silent> <Leader>' :Marks<CR>
nnoremap <silent> <Leader>g :Commits<CR>
nnoremap <silent> <Leader>H :Helptags<CR>
nnoremap <silent> <Leader>hh :History<CR>
nnoremap <silent> <Leader>h: :History:<CR>
nnoremap <silent> <Leader>h/ :History/<CR>

用Rg取代Grep

之前提到过两种在多个文件内搜索地指令:vim:grep

2021/10/04
> CLICK TO back <